#define LOG_MODULE_ID ""

#include "log.h"
#include <sys/time.h>
#include <sys/syscall.h>
#include <unistd.h>

#include <stdarg.h>
#include <string.h>
#include <time.h>
#include <stdio.h>

#include <mutex>

#define TIMESTAMP_STRING_SIZE   22
#define LOG_MAX_LEN   (100 << 10)

namespace {
  //! 日志内容
  struct LogContent {
    long thread_id;         //!< 线程ID
    struct {
      uint32_t sec;         //!< 秒
      uint32_t usec;        //!< 微秒
    } timestamp;            //!< 时间戳

    const char *module_id;  //!< 模块名
    const char *func_name;  //!< 函数名
    const char *file_name;  //!< 文件名
    int         line;       //!< 行号
    int         level;      //!< 日志等级
    uint32_t    text_len;   //!< 内容大小
    const char *text_ptr;   //!< 内容地址
  };

  std::mutex _lock;

  const char *_level_name = "FEWNIDT";
  const int _level_color_num[] = {31, 91, 93, 33, 32, 36, 35};

  void _GetCurrTimeString(const LogContent *content, char *timestamp)
  {
#if 1
    time_t ts_sec = content->timestamp.sec;
    struct tm tm;
    localtime_r(&ts_sec, &tm);
    char tmp[15];
    strftime(tmp, sizeof(tmp), "%m-%d %H:%M:%S", &tm);
    snprintf(timestamp, TIMESTAMP_STRING_SIZE, "%s.%06u", tmp, content->timestamp.usec);
#else
    snprintf(timestamp, TIMESTAMP_STRING_SIZE, "%u.%06u", content->timestamp.sec, content->timestamp.usec);
#endif
  }

  const char* _Basename(const char *full_path)
  {
    const char *p_last = full_path;
    if (p_last != nullptr) {
      for (const char *p = full_path; *p; ++p) {
        if (*p == '/')
          p_last = p + 1;
      }
    }
    return p_last;
  }


  void _PrintLogToStdout(const LogContent *content)
  {
    std::lock_guard<std::mutex> lg(_lock);

    char timestamp[TIMESTAMP_STRING_SIZE]; //!  "05-13 23:45:07.000000"
    _GetCurrTimeString(content, timestamp);

    //! 开启色彩，显示日志等级
    printf("\033[%dm%c ", _level_color_num[content->level], _level_name[content->level]);

    //! 打印时间戳、线程号、模块名
    printf("%s %ld %s ", timestamp, content->thread_id, content->module_id);

    if (content->func_name != nullptr)
      printf("%s() ", content->func_name);

    printf("%s ", content->text_ptr);

    if (content->file_name != nullptr) {
      printf("-- %s:%d", content->file_name, content->line);
    }

    puts("\033[0m");    //! 恢复色彩
  }
}

  extern "C"
void LogPrintfFunc(const char *module_id, const char *func_name, const char *file_name,
    int line, int level, bool with_args, const char *fmt, ...)
{
  if (level < 0) level = 0;
  if (level > LOG_LEVEL_TRACE) level = LOG_LEVEL_TRACE;

  const char *module_id_be_print = (module_id != nullptr) ? module_id : "???";

  struct timeval tv;
  struct timezone tz;
  gettimeofday(&tv, &tz);

  LogContent content = {
    .thread_id = syscall(SYS_gettid),
    .timestamp = {
      .sec  = static_cast<uint32_t>(tv.tv_sec),
      .usec = static_cast<uint32_t>(tv.tv_usec),
    },
    .module_id = module_id_be_print,
    .func_name = func_name,
    .file_name = _Basename(file_name),
    .line = line,
    .level = level,
    .text_len = 0,
    .text_ptr = nullptr,
  };

  if (fmt != nullptr) {
    if (with_args) {
      uint32_t buff_size = 1024;    //! 初始大小，可应对绝大数情况
      for (;;) {
        va_list args;
        char buffer[buff_size];

        va_start(args, fmt);
        size_t len = vsnprintf(buffer, buff_size, fmt, args);
        va_end(args);

        if (len < buff_size) {
          content.text_len = len;
          content.text_ptr = buffer;
          _PrintLogToStdout(&content);
          break;
        }

        buff_size = len + 1;    //! 要多留一个结束符 \0，否则 vsnprintf() 会少一个字符
        if (buff_size > LOG_MAX_LEN) {
          printf("WARN: log text length %u, too long!\n", buff_size);
          break;
        }
      }
    } else {
      content.text_len = ::strlen(fmt);
      content.text_ptr = fmt;
      _PrintLogToStdout(&content);
    }
  } else {
    _PrintLogToStdout(&content);
  }
}

